import React, { useState, useEffect, useRef, ReactElement } from 'react';
import { STATUS } from './interface.d';
import useUpdateEffect from './useUpdateEffect';

export interface TransitionProps {
  /** 控制组件的进场、退场动画执行 */
  in?: boolean;
  /** 动画的持续时间 */
  timeout: number;
  /** 结束动画完毕后是否销毁组件 */
  unmountOnExit?: boolean;
  /** 收否在组件初次渲染的时的进场、退场动画（默认true，是执行的） */
  isPerformInitAnimation?: boolean;
  children: ((status: STATUS) => ReactElement) | ReactElement;
  /** 动画开始前 */
  onEnter?(node?: HTMLElement | null, isAppear?: boolean): void;
  /** 动画进入后 onEnterAfter/*/
  onEntering?(node?: HTMLElement | null, isAppear?: boolean): void;
  onEntered?(node?: HTMLElement | null, isAppear?: boolean): void;
  onExit?(node?: HTMLElement | null): void;
  onExiting?(node?: HTMLElement | null): void;
  onExited?(node?: HTMLElement | null): void;
}

export const Transition = (props: TransitionProps) => {
  const {
    timeout,
    in: inProps,
    unmountOnExit,
    children,
    isPerformInitAnimation = true,
    onEnter,
    onEntering,
    onEntered,
    onExit,
    onExiting,
    onExited,
    ...otherProps
  } = props;
  // 如果初始化就执行动画，那么status的默认值分别是ENTER/EXIT，否则是ENTERED/EXITED

  const [status, setStatus] = useState<STATUS>(inProps ? STATUS.enter : STATUS.exit);
  const enteredTimerRef = useRef<any>();
  const exitedTimerRef = useRef<any>();

  /** 开始进场动画 */
  function startEnterAnimation() {
    onEnter?.();
    // 如果当前已经是初始化状态，那么就直接变成ENTERING状态
    requestIdleCallback(() => {
      setStatus(STATUS.enter);
    })
  }

  function startExitAnimation() {
    onExit?.();
    setStatus(STATUS.exit);
  }

  useEffect(() => {
    switch (status) {
      case STATUS.enter: {
        setStatus(STATUS.entering);
        // 清除上次的退场动画结束
        clearTimeout(exitedTimerRef.current);
        break;
      }
      case STATUS.entering: {
        onEntering?.();
        enteredTimerRef.current = setTimeout(() => {
          setStatus(STATUS.entered);
          onEntered?.();
        }, timeout);
        break;
      }
      case STATUS.exit: {
        setStatus(STATUS.exiting);
        // 每次都需要清除上次的进场动画结束的定时器，防止同时执行了退场动画又执行进场动画
        clearTimeout(enteredTimerRef.current);
        break;
      }
      case STATUS.exiting: {
        onExiting?.();
        exitedTimerRef.current = setTimeout(() => {
          setStatus(STATUS.exited);
          onExited?.();
        }, timeout);
        break;
      }
    }
  }, [status]);

  useUpdateEffect(() => {
    if (inProps) {
      startEnterAnimation();
    } else {
      startExitAnimation();
    }
  }, [inProps]);

  useEffect(() => {
    if (isPerformInitAnimation) {
      inProps ? setStatus(STATUS.entering) : setStatus(STATUS.exiting);
    }
  }, []);

  if (unmountOnExit && status === STATUS.exited) {
    return null;
  }

  if (typeof children === 'function') {
    return children(status);
  }

  return React.isValidElement(children)
    ? React.cloneElement(children, otherProps)
    : children;
};
